home *** CD-ROM | disk | FTP | other *** search
- Path: xanth!mcnc!gatech!bloom-beacon!tut.cis.ohio-state.edu!husc6!necntc!ncoast!allbery
- From: ljz@ames.arc.nasa.gov@@uunet.uu.net:fxgrp.UUCP
- Newsgroups: comp.sources.misc
- Subject: v03i016: egetopt: extended getopt()
- Message-ID: <8805140225.AA09152@fxgrp.fx.com>
- Date: 14 May 88 02:25:27 GMT
- Sender: allbery@ncoast.UUCP
- Reply-To: ljz@ames.arc.nasa.gov@@uunet.uu.net:fxgrp.UUCP
- Lines: 636
- Approved: allbery@ncoast.UUCP
-
- comp.sources.misc: Volume 3, Issue 16
- Submitted-By: "A. Nonymous" <ljz@ames.arc.nasa.gov@@uunet.uu.net:fxgrp.UUCP>
- Archive-Name: egetopt
-
- I hope you'll see fit to post this to comp.sources.unix:
-
- Due to the recent discussion in comp.lang.c about the deficiencies of
- getopt(), I have created an extended getopt() called "egetopt()". Its
- default behavior is exactly the same as that of getopt(), but
- interested users can also make use of several useful extensions.
-
- For those of you who believe getopt() is perfect and should never be
- improved, egetopt() will mirror its behavior. For those of you who
- would like something better, I hope you will be satisfied with the
- added capabilities of egetopt().
-
- This has made it through testing, but it may still have a bug or two.
- It has no man page as yet ... would some kind soul be willing to write
- one? It does have a README file which describes its features,
- however.
-
- Enjoy.
-
- --
- Lloyd Zusman
- Master Byte Software
- Los Gatos, California Internet: ljz@fx.com
- "We take things well in hand." UUCP: ...!ames!fxgrp!ljz
-
- #--------------------------Cut Here--------------------------
- #! /bin/sh
- # This is a shell archive. Remove anything before the "#! /bin/sh" line,
- # then unpack it by saving it in a file and typing "sh file."
- #
- # Wrapped by Lloyd Zusman (ljz) at fxgrp on Fri May 13 19:08:18 1988
- #
- # unpacks with default permissions
- #
- # Contents : README Makefile demo.c egetopt.c
- #
- if `test ! -s README`
- then
- echo "x - README"
- sed 's/^X//' > README << '@\END_OF_FILE_README'
- Xegetopt.c -- Extended 'getopt'.
- X
- XA while back, a public-domain version of getopt() was posted to the
- Xnet. A bit later, a gentleman by the name of Keith Bostic made some
- Xenhancements and reposted it.
- X
- XIn recent weeks (i.e., early-to-mid 1988) there has been a sometimes
- Xheated discussion in comp.lang.c about the merits and drawbacks of
- Xgetopt(), especially with regard to its handling of '?'.
- X
- XIn light of this, I have taken Mr. Bostic's public-domain getopt()
- Xand have made some changes that I hope will be considered to be
- Ximprovements. I call this routine 'egetopt' ("Extended getopt").
- XThe default behavior of this routine is the same as that of getopt(),
- Xbut it has some optional features that make it more useful. These
- Xoptions are controlled by the settings of some global variables.
- XBy not setting any of these extra global variables, you will have
- Xthe same functionality as getopt(), which should satisfy those
- Xpurists who believe getopt() is perfect and can never be improved.
- XIf, on the other hand, you are someone who isn't satisfied with the
- Xstatus quo, egetopt() may very well give you the added capabilities
- Xyou want.
- X
- Xegetopt() behaves like getopt() with the following added capabilities:
- X
- X-- The '?' which gets returned when there is an unrecognized option
- X is now stored in a global integer called 'optbad', and the caller
- X can set this value to anything. The initial value in 'optbad' is
- X '?', which means that the default behavior is just like that of
- X getopt(). For example, If you want egetopt() to return '~'
- X instead of '?' when it sees an invalid option, put the following
- X lines in your code before egetopt() gets called:
- X
- X extern int optbad;
- X optbad = (int)'~';
- X
- X-- Options can begin with characters other than just '-'. There
- X is now a global character pointer called 'optstart'. It points
- X to a string which consists of a list of characters which can
- X be used to begin options. The initial string that 'optstart'
- X points to is "-", so the default behavior is like that of
- X getopt(). For example, if you want to allow both '+' and '-'
- X as option delimiters, put the following lines in your code
- X before egetopt() gets called:
- X
- X extern char *optstart;
- X optstart = "-+";
- X
- X-- Now that there's a choice of the characters that can precede options
- X it's desirable to let the caller know what character begins a
- X given option. In egetopt(), the global integer 'optchar' will
- X now contain the character that begins a given option, or 0 if
- X there was an error. Just put the following line in your code
- X and you can check the value of 'optchar' after each call to
- X egetopt():
- X
- X extern int optchar;
- X
- X-- The old getopt() writes error messages to file descriptor 2
- X (or to stderr, depending on your implementation). In egetopt(),
- X you can change this file descriptor to be anything you want.
- X The global integer 'opterrfd' contains the file descriptor
- X to use for writing error messages. As you might have guessed,
- X this variable is initialized to 2. As an example, if you want
- X your egetopt() errors to go to the file "egetopt.errs", do
- X something similar to the following before calling egetopt():
- X
- X extern int opterrfd;
- X
- X FILE *eout = fopen("egetopt.errs", "w");
- X
- X if (eout == (FILE)NULL) {
- X /* error condition/
- X ...
- X exit(1);
- X }
- X
- X opterrfd = fileno(eout);
- X
- X-- Some implementations of getopt() allow you to set the global
- X integer 'opterr' to control whether error output is printed:
- X it is initialized to 1, which enables error output (as does
- X any non-zero value); setting it to 0 disables error output.
- X In egetopt(), 'opterr' is treated the same way.
- X
- X-- The old getopt() forces you to use ':' in the string of option
- X letters to show that a given option takes an argument. There is
- X now a global integer called 'optneed' which contains this value,
- X so you can change it to something else if you want. As you might
- X have suspected, 'optneed' is initialized to ':'.
- X
- X In addition, something that I always found annoying about the old
- X getopt() is its inability to handle non-mandatory option arguments.
- X For example, if an option called 'd' was specified as taking
- X an argument to the program 'foo', you'd get the following
- X results when invoking 'foo' in different ways:
- X
- X 1) foo -dABC -x ...
- X
- X getopt() return: 'd'
- X optarg: "ABC"
- X
- X 2) foo -dABC -x ...
- X
- X getopt() return: 'd'
- X optarg: "ABC"
- X
- X 3) foo -d -x ...
- X
- X A) getopt() return: 'd'
- X optarg: "-x"
- X
- X In the case of number 3, sometimes one would prefer to get ...
- X
- X B) getopt() return: 'd'
- X optarg: NULL
- X
- X This would allow "-x" to be handled as another option in the next
- X call. In the old getopt(), you can get the 3B behavior by testing
- X the first character of 'optarg' and decrementing 'optind' if this
- X character is '-'. However, since I am enhancing the routine
- X anyway, I decided to build in the ability to have either the 3A
- X or the 3B behavior.
- X
- X Since this behavior isn't always desired, I have added another
- X global integer called 'optmaybe' which optionally allows you to
- X control whether an option with an argument will get treated as number
- X 3A or as number 3B above. It is used similarly to 'optneed'. It is
- X initialized to 0, meaning that behavior 3B is impossible in the
- X default case. The following example shows how 'optneed' and
- X 'optmaybe' can be used:
- X
- X extern int optneed;
- X extern int optmaybe;
- X
- X optneed = (int)'!'; /* use '!' instead of ':'/
- X optmaybe = (int)'%'; /* use '%' for optional arguments/
- X
- X ...
- X
- X while ((c = egetopt(argc, argv, "abc!d%x")) != EOF) ...
- X
- X In this example, options 'a', 'b', and 'x' take no arguments,
- X option 'c' takes a mandatory argument, and option 'd' takes
- X a non-mandatory argument. If this is contained in program 'foo',
- X you'll get the following behavior when you run it:
- X
- X foo -a -cABC -dXYZ -d -x -c -b ...
- X
- X egetopt() return: 'a'
- X optarg: NULL
- X
- X egetopt() return: 'c'
- X optarg: "ABC"
- X
- X egetopt() return: 'd'
- X optarg: "XYZ"
- X
- X >>>>>>>>>> egetopt() return: 'd'
- X >>>>>>>>>> optarg: NULL
- X >> NOTE >>
- X >>>>>>>>>> egetopt() return: 'x'
- X >>>>>>>>>> optarg: NULL
- X
- X egetopt() return: 'c'
- X optarg: "-b"
- X
- X ...
- X
- X Remember that 'optneed' is initialized to ':' and 'optmaybe'
- X is initialized to 0. This causes behavior identical to that
- X of getopt() unless you specifically override it.
- X
- XSince the default behavior of egetopt() is the same as that of getopt(),
- Xthere is no reason why you can't rename this routine to getopt() and
- Xuse it in place of the original. I gave it a new name so as not to
- Xoffend those of you who believe that getopt() is perfect and should
- Xnever have any new features added to it.
- X
- XThe code was originally posted to the net as getopt.c by ...
- X
- X Keith Bostic
- X ARPA: keith@seismo
- X UUCP: seismo!keith
- X
- XCurrent version: added enhancements and comments, reformatted code.
- X
- X Lloyd Zusman
- X Master Byte Software
- X Los Gatos, California
- X Internet: ljz@fx.com
- X UUCP: ...!ames!fxgrp!ljz
- X
- X May, 1988
- @\END_OF_FILE_README
- else
- echo "shar: Will not over write README"
- fi
- if `test ! -s Makefile`
- then
- echo "x - Makefile"
- sed 's/^X//' > Makefile << '@\END_OF_FILE_Makefile'
- X#
- X# Makefile for building egetopt.o and a program that demonstrates it.
- X#
- XCFLAGS =-g
- X
- Xall: demo
- X
- Xegetopt.o: egetopt.c
- X
- Xdemo.o: demo.c
- X
- Xdemo: demo.o egetopt.o
- X cc -o demo demo.o egetopt.o
- @\END_OF_FILE_Makefile
- else
- echo "shar: Will not over write Makefile"
- fi
- if `test ! -s demo.c`
- then
- echo "x - demo.c"
- sed 's/^X//' > demo.c << '@\END_OF_FILE_demo.c'
- X#include <stdio.h>
- X
- X/*
- X * This is a short program for demonstrating the capabilities of egetopt().
- X * Run it with various combinations of options and arguments on the command
- X * line to see how egetopt() works.
- X *
- X * Experiment around with this by changing some of my settings and
- X * recompiling.
- X */
- X
- X#define OPT_STRING "abc~d~e?f?"
- X/* Meaning:
- X *
- X * -a and -b take no arguments.
- X * -c and -d take mandatory arguments (I set 'optneed' to '~', below).
- X * -e and -f take optional arguments (I set 'optmaybe' to '?', below).
- X */
- X
- X#define OPT_CHARS "-+="
- X/* Meaning:
- X *
- X * Options can begin with '-', '+', or '='.
- X */
- X
- X
- X/*
- X * New global variables used in egetopt() only:
- X */
- Xextern int optneed; /* character used for mandatory arguments */
- Xextern int optmaybe; /* character used for optional arguments */
- Xextern int optchar; /* character which begins a given argument */
- Xextern int optbad; /* what egetopt() returns for a bad option */
- Xextern int opterrfd; /* where egetopt() error messages go */
- Xextern char *optstart; /* string which contains valid option start chars */
- X
- X/*
- X * Global variables which exist in getopt() and egetopt():
- X */
- Xextern int optind; /* index of current argv[] */
- Xextern int optopt; /* the actual option pointed to */
- Xextern int opterr; /* set to 0 to suppress egetopt's error messages */
- Xextern char *optarg; /* the argument of the option */
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int ch;
- X
- X opterrfd = fileno(stdout); /* errors to stdout */
- X opterr = 0; /* set this to 1 to get egetopt's error msgs */
- X optbad = '!'; /* return '!' instead of '?' on error */
- X optneed = '~'; /* mandatory arg identifier (in OPT_STRING) */
- X optmaybe = '?'; /* optional arg identifier (in OPT_STRING) */
- X optstart = OPT_CHARS; /* characters that can start options */
- X
- X while ((ch = egetopt(argc, argv, OPT_STRING)) != EOF) {
- X printf("\n\toption index (optind) after egetopt(): %5d\n",
- X optind);
- X printf("\t\tegetopt() return value: %c (%d)\n",
- X ch, ch);
- X printf("\t\tchar that begins option (optchar): %c\n",
- X optchar);
- X printf("\t\tactual char looked at (optopt): %c\n",
- X optopt);
- X printf("\t\toption argument: \"%s\"\n",
- X optarg == NULL ? "(null)" : optarg);
- X }
- X
- X for (; optind < argc; ++optind) {
- X printf("\n\targument index %5d\n",
- X optind);
- X printf("\t\targument: \"%s\"\n",
- X argv[optind] == NULL ? "(null)" : argv[optind]);
- X }
- X
- X exit(0);
- X}
- @\END_OF_FILE_demo.c
- else
- echo "shar: Will not over write demo.c"
- fi
- if `test ! -s egetopt.c`
- then
- echo "x - egetopt.c"
- sed 's/^X//' > egetopt.c << '@\END_OF_FILE_egetopt.c'
- X/*
- X * egetopt.c -- Extended 'getopt'.
- X *
- X * A while back, a public-domain version of getopt() was posted to the
- X * net. A bit later, a gentleman by the name of Keith Bostic made some
- X * enhancements and reposted it.
- X *
- X * In recent weeks (i.e., early-to-mid 1988) there's been some
- X * heated discussion in comp.lang.c about the merits and drawbacks
- X * of getopt(), especially with regard to its handling of '?'.
- X *
- X * In light of this, I have taken Mr. Bostic's public-domain getopt()
- X * and have made some changes that I hope will be considered to be
- X * improvements. I call this routine 'egetopt' ("Extended getopt").
- X * The default behavior of this routine is the same as that of getopt(),
- X * but it has some optional features that make it more useful. These
- X * options are controlled by the settings of some global variables.
- X * By not setting any of these extra global variables, you will have
- X * the same functionality as getopt(), which should satisfy those
- X * purists who believe getopt() is perfect and can never be improved.
- X * If, on the other hand, you are someone who isn't satisfied with the
- X * status quo, egetopt() may very well give you the added capabilities
- X * you want.
- X *
- X * Look at the enclosed README file for a description of egetopt()'s
- X * new features.
- X *
- X * The code was originally posted to the net as getopt.c by ...
- X *
- X * Keith Bostic
- X * ARPA: keith@seismo
- X * UUCP: seismo!keith
- X *
- X * Current version: added enhancements and comments, reformatted code.
- X *
- X * Lloyd Zusman
- X * Master Byte Software
- X * Los Gatos, California
- X * Internet: ljz@fx.com
- X * UUCP: ...!ames!fxgrp!ljz
- X *
- X * May, 1988
- X */
- X
- X/*
- X * If you want, include stdio.h or something where EOF and NULL are defined.
- X * However, egetopt() is written so as not to need stdio.h, which should
- X * make it significantly smaller on some systems.
- X */
- X
- X#ifndef EOF
- X# define EOF (-1)
- X#endif /* ! EOF */
- X
- X#ifndef NULL
- X# define NULL (char *)0
- X#endif /* ! NULL */
- X
- X/*
- X * None of these constants are referenced in the executable portion of
- X * the code ... their sole purpose is to initialize global variables.
- X */
- X#define BADCH (int)'?'
- X#define NEEDSEP (int)':'
- X#define MAYBESEP (int)'\0'
- X#define ERRFD 2
- X#define EMSG ""
- X#define START "-"
- X
- X/*
- X * Here are all the pertinent global variables.
- X */
- Xint opterr = 1; /* if true, output error message */
- Xint optind = 1; /* index into parent argv vector */
- Xint optopt; /* character checked for validity */
- Xint optbad = BADCH; /* character returned on error */
- Xint optchar = 0; /* character that begins returned option */
- Xint optneed = NEEDSEP; /* flag for mandatory argument */
- Xint optmaybe = MAYBESEP;/* flag for optional argument */
- Xint opterrfd = ERRFD; /* file descriptor for error text */
- Xchar *optarg; /* argument associated with option */
- Xchar *optstart = START; /* list of characters that start options */
- X
- X
- X/*
- X * Macros.
- X */
- X
- X/*
- X * Conditionally print out an error message and return (depends on the
- X * setting of 'opterr' and 'opterrfd'). Note that this version of
- X * TELL() doesn't require the existence of stdio.h.
- X */
- X#define TELL(S) { \
- X if (opterr && opterrfd >= 0) { \
- X char option = optopt; \
- X write(opterrfd, *nargv, strlen(*nargv)); \
- X write(opterrfd, (S), strlen(S)); \
- X write(opterrfd, &option, 1); \
- X write(opterrfd, "\n", 1); \
- X } \
- X return (optbad); \
- X}
- X
- X/*
- X * This works similarly to index() and strchr(). I include it so that you
- X * don't need to be concerned as to which one your system has.
- X */
- Xstatic char *
- X_sindex(string, ch)
- Xchar *string;
- Xint ch;
- X{
- X if (string != NULL) {
- X for (; *string != '\0'; ++string) {
- X if (*string == (char)ch) {
- X return (string);
- X }
- X }
- X }
- X
- X return (NULL);
- X}
- X
- X/*
- X * Here it is:
- X */
- Xint
- Xegetopt(nargc, nargv, ostr)
- Xint nargc;
- Xchar **nargv;
- Xchar *ostr;
- X{
- X static char *place = EMSG; /* option letter processing */
- X register char *oli; /* option letter list index */
- X register char *osi = NULL; /* option start list index */
- X
- X if (nargv == (char **)NULL) {
- X return (EOF);
- X }
- X
- X if (nargc <= optind || nargv[optind] == NULL) {
- X return (EOF);
- X }
- X
- X if (place == NULL) {
- X place = EMSG;
- X }
- X
- X /*
- X * Update scanning pointer.
- X */
- X if (*place == '\0') {
- X place = nargv[optind];
- X if (place == NULL) {
- X return (EOF);
- X }
- X osi = _sindex(optstart, *place);
- X if (osi != NULL) {
- X optchar = (int)*osi;
- X }
- X if (optind >= nargc || osi == NULL || *++place == '\0') {
- X return (EOF);
- X }
- X
- X /*
- X * Two adjacent, identical flag characters were found.
- X * This takes care of "--", for example.
- X */
- X if (*place == place[-1]) {
- X ++optind;
- X return (EOF);
- X }
- X }
- X
- X /*
- X * If the option is a separator or the option isn't in the list,
- X * we've got an error.
- X */
- X optopt = (int)*place++;
- X oli = _sindex(ostr, optopt);
- X if (optopt == optneed || optopt == optmaybe || oli == NULL) {
- X /*
- X * If we're at the end of the current argument, bump the
- X * argument index.
- X */
- X if (*place == '\0') {
- X ++optind;
- X }
- X TELL(": illegal option -- "); /* byebye */
- X }
- X
- X /*
- X * If there is no argument indicator, then we don't even try to
- X * return an argument.
- X */
- X ++oli;
- X if (*oli == '\0' || (*oli != optneed && *oli != optmaybe)) {
- X /*
- X * If we're at the end of the current argument, bump the
- X * argument index.
- X */
- X if (*place == '\0') {
- X ++optind;
- X }
- X optarg = NULL;
- X }
- X /*
- X * If we're here, there's an argument indicator. It's handled
- X * differently depending on whether it's a mandatory or an
- X * optional argument.
- X */
- X else {
- X /*
- X * If there's no white space, use the rest of the
- X * string as the argument. In this case, it doesn't
- X * matter if the argument is mandatory or optional.
- X */
- X if (*place != '\0') {
- X optarg = place;
- X }
- X /*
- X * If we're here, there's whitespace after the option.
- X *
- X * Is it a mandatory argument? If so, return the
- X * next command-line argument if there is one.
- X */
- X else if (*oli == optneed) {
- X /*
- X * If we're at the end of the argument list, there
- X * isn't an argument and hence we have an error.
- X * Otherwise, make 'optarg' point to the argument.
- X */
- X if (nargc <= ++optind) {
- X place = EMSG;
- X TELL(": option requires an argument -- ");
- X }
- X else {
- X optarg = nargv[optind];
- X }
- X }
- X /*
- X * If we're here it must have been an optional argument.
- X */
- X else {
- X if (nargc <= ++optind) {
- X place = EMSG;
- X optarg = NULL;
- X }
- X else {
- X optarg = nargv[optind];
- X if (optarg == NULL) {
- X place = EMSG;
- X }
- X /*
- X * If the next item begins with a flag
- X * character, we treat it like a new
- X * argument. This is accomplished by
- X * decrementing 'optind' and returning
- X * a null argument.
- X */
- X else if (_sindex(optstart, *optarg) != NULL) {
- X --optind;
- X optarg = NULL;
- X }
- X }
- X }
- X place = EMSG;
- X ++optind;
- X }
- X
- X /*
- X * Return option letter.
- X */
- X return (optopt);
- X}
- @\END_OF_FILE_egetopt.c
- else
- echo "shar: Will not over write egetopt.c"
- fi
- # to concatenate archives, remove anything after this line
- exit 0
-